package gsar.client;

import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;


/*
Copyright (c) 2009, Eric Eaton, University of Maryland Baltimore County
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of the University of Maryland Baltimore County nor the
      names of its contributors may be used to endorse or promote products
      derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/** Represents a gridworld map.
 * 
 * @author Eric Eaton
 */
public class GWMap {
	
	/** A map from a coordinate to a list of the coordinates adjacent to that coordinate on the map. */
	private HashMap<GWCoordinate, Set<GWCoordinate>> m_adjacentCoordinates = null;
	
	/** A map from a marker to a list of the coordinates with the marker.*/
	private HashMap<String, Set<GWCoordinate>> m_markedCoordinates = null;

	
	
	
	/**
	 * Given a list of cells, initializes the map.
	 * @param mapcells a list of cells
	 */
	public GWMap(Collection<Mapcell> mapcells) {
		
		// reinitialize the two main data structures
		m_adjacentCoordinates = new HashMap<GWCoordinate, Set<GWCoordinate>> ();
		m_markedCoordinates = new HashMap<String, Set<GWCoordinate>>();
		
		HashMap<String, GWCoordinate> idToCoordinates = new HashMap<String, GWCoordinate>();
		
		// loop through the mapcells to initialize the hash of cell id to coordinates
		for (Mapcell x : mapcells) {
			idToCoordinates.put(x.getCellID(), x.getCoordinates());
		}

		// loop again through the mapcells, processing each mapcell and resolving references
		for (Mapcell x : mapcells) {
			
			// convert the adjacent cell references to coordinates and hash those as the adjacent cells
			Set<GWCoordinate> tempAdjCoords = new HashSet<GWCoordinate>();
			for (String adjCellID : x.getAdjacentMapcells()) {
				tempAdjCoords.add(idToCoordinates.get(adjCellID));
			}
			m_adjacentCoordinates.put(x.getCoordinates(), tempAdjCoords);

			// keep track of the various markers
			String marker = x.getMarker();
			if (marker != null) {
				if (!m_markedCoordinates.containsKey(marker))
					m_markedCoordinates.put(marker, new HashSet<GWCoordinate>());
				Set<GWCoordinate> similarCells = m_markedCoordinates.get(marker);
				similarCells.add(x.getCoordinates());
				m_markedCoordinates.put(marker, similarCells);
			}
		}
	}


	/** Retrieves a list of the coordinates adjacent to the given coordinate on the map.
	 * @param coord the coordinate
	 * @return a list of coordinates adjacent to the given coordinate.  If the list is empty,
	 *         then the coordinate is isolated; if the return value is null, then there is
	 *         nothing known about the adjacency of the coordinate.
	 */
	public Set<GWCoordinate> getAdjacentCoordinates(GWCoordinate coord) {
		return m_adjacentCoordinates.get(coord);
	}

	/** Determines whether two coordinates are adjacent on the map.
	 * @param coord1 the coordinates to test
	 * @param coord2 the coordinates to test
	 * @return whether the coordinates are adjacent on the map, null if nothing is known about either coordinate
	 */
	public boolean isAdjacent(GWCoordinate coord1, GWCoordinate coord2) {

		Set<GWCoordinate> adjacentCoordinates = m_adjacentCoordinates.get(coord1);
		if (adjacentCoordinates == null) return false;
		return adjacentCoordinates.contains(coord2);
	}



	/** Add an adjacency between two coordinates.
	 * @param coord1 the first coordinate
	 * @param coord2 the second coordinate
	 */
	public void addAdjacency(GWCoordinate coord1, GWCoordinate coord2) {
		
		// ensure that the adjacency lists aren't null
		if (m_adjacentCoordinates.get(coord1) == null) {
			m_adjacentCoordinates.put(coord1, new HashSet<GWCoordinate>());
		}
		if (m_adjacentCoordinates.get(coord2) == null) {
			m_adjacentCoordinates.put(coord2, new HashSet<GWCoordinate>());
		}
		
		// add the adjacency to coord1
		m_adjacentCoordinates.get(coord1).add(coord2);
	
		// add the adjacency to coord2
		m_adjacentCoordinates.get(coord2).add(coord1);
		
	}


	/** Removes an adjacency between two coordinates
	 * @param coords1 the first coordinate
	 * @param coords2 the second coordinate
	 */
	public void removeAdjacency(GWCoordinate coords1, GWCoordinate coords2) {
		m_adjacentCoordinates.get(coords1).remove(coords2);
		m_adjacentCoordinates.get(coords2).remove(coords2);
	}
	
	
	/** Retrieves a list of the coordinates with the given marker.
	 * @param marker the marker to match
	 * @return a list of coordinates with the given marker
	 */
	public Set<GWCoordinate> getMarkedCoordinates(String marker) {
		return m_markedCoordinates.get(marker);
	}
	
	/** Determines whether the given coordinate has the given marker
	 * @param coord the coordinate to test
	 * @param marker the marker
	 * @return whether the given coordinate has the given marker
	 */
	public boolean hasMarker(GWCoordinate coord, String marker) {
		Set<GWCoordinate> markedCoordinates = m_markedCoordinates.get(marker);
		if (markedCoordinates == null)
			return false;
		return markedCoordinates.contains(coord);
	}
	
	/** Add a marker on the given coordinates
	 * @param coord the coordinate
	 * @param marker the marker
	 */
	public void addMarker(GWCoordinate coord, String marker) {
		if (!m_markedCoordinates.containsKey(marker))
			m_markedCoordinates.put(marker, new HashSet<GWCoordinate>());
		m_markedCoordinates.get(marker).add(coord);
	}

	/** Remove a marker on the given coordinates
	 * @param coord the coordinate
	 * @param marker the marker
	 */
	public void removeMarker(GWCoordinate coord, String marker) {
		if (m_markedCoordinates.containsKey(marker))
			m_markedCoordinates.get(marker).remove(coord);
	}
	
	
	
	/** Helper data structure to represent each cell of the map.
	 * 
	 * @author Eric Eaton
	 */
	public static class Mapcell {
		GWCoordinate m_coordinates;
		String m_cellID;
		List<String> m_adjacentMapcells;
		String m_marker;
 
		/** Constructor
		 * @param coordinates the coordinates of the mapcell
		 * @param cellID the unique id of the mapcell
		 * @param adjacentMapcells a list of adjacent mapcells by cellID
		 * @param marker the marker on the mapcell
		 */
		public Mapcell(GWCoordinate coordinates, String cellID, List<String> adjacentMapcells, String marker) {
			m_coordinates = coordinates;
			m_cellID = cellID;
			m_adjacentMapcells = adjacentMapcells;
			m_marker = marker;
		}

		/** Get the coordinates of the mapcell
		 * @return the coordinates of the mapcell
		 */
		public GWCoordinate getCoordinates() { return m_coordinates; }
		
		/** Gets the unique id of the mapcell.
		 * @return the id of the mapcell
		 */
		public String getCellID() { return m_cellID; }
		
		/** Gets the list of adjacent mapcells' ids
		 * @return a list of the adjacent mapcells' ids
		 */
		public List<String> getAdjacentMapcells() { return m_adjacentMapcells; }
		
		/** Gets the marker on this mapcell.
		 * @return the marker on this mapcell
		 */
		public String getMarker() { return m_marker; }

	}
	
	
	
	
	
}
